#include "assert.h"
#include "tlb.h"

//invalidate all unlocked entries in the TLB
void CTLB::adj_age(char new_youngest)
{
	if(new_youngest==last_youngest) return;


	for(int i=0;i<N_TLB_ENTRIES;i++)
	{
		
		if(ages[i]<N_TLB_ENTRIES-1) ages[i]++;
	}
	ages[new_youngest]=0;
	last_youngest=new_youngest;
}

//invalidate all unlocked entries in the TLB
void CTLB::invlidate_all()
{
	for(int i=0;i<N_TLB_ENTRIES;i++)
	{
		tlb[i].valid=0;
		ages[i]=N_TLB_ENTRIES-1;
	}
	last_youngest=-1;
}
//invalidate a single TLB entry, by MVA for a global entry
void CTLB::invlidate_mva(__u32 mva)
{
	for(int i=0;i<N_TLB_ENTRIES;i++)
	{
		if(tlb[i].valid!=0
			&&tlb[i].va32<=mva
			&&(tlb[i].va32+tlb[i].blk_size>mva))
		{
			tlb[i].valid=0;
			ages[i]=N_TLB_ENTRIES-1;
		}
	}
}
//invalidate a single TLB entry, by  MVA and ASID for a non-global entry
void CTLB::invlidate_mva(__u32 mva,__u32 asid)
{
	for(int i=0;i<N_TLB_ENTRIES;i++)
	{
		if(tlb[i].valid!=0
			&&tlb[i].va32<=mva
			&&(tlb[i].va32+tlb[i].blk_size>mva)
			&& (tlb[i].nnG)||tlb[i].asid==asid)
		{
			tlb[i].valid=0;
			ages[i]=N_TLB_ENTRIES-1;
		}
	}
}
//invalidate all TLB entries that match a specified ASID.
void CTLB::invlidate_asid(__u32 asid)
{
	for(int i=0;i<N_TLB_ENTRIES;i++)
	{
		if(tlb[i].valid!=0
			&& (tlb[i].nnG)||tlb[i].asid==asid)
		{
			tlb[i].valid=0;
			ages[i]=N_TLB_ENTRIES-1;
		}
	}
}

TLB_ECODE CTLB::lookup(__u32 mva,__u32 asid,TLB_ENTRY * p_tlb_entry)
{
	for(int i=0;i<N_TLB_ENTRIES;i++)
	{
		if(tlb[i].valid!=0
			&&tlb[i].va32<=mva
			&&(tlb[i].va32+tlb[i].blk_size>mva)
			&&(tlb[i].nnG||tlb[i].asid==asid))
		{
			*p_tlb_entry=tlb[i];
			adj_age(i);
			return TLB_HIT;
		}
	}

	return TLB_MISS;
}
void CTLB::update_translation(TLB_ENTRY * p_translation)
{
	int i;
	//
	// find a free slot, or drop lu entry
	//
	for( i=0;i<N_TLB_ENTRIES;i++)
	{
		if(ages[i]>=N_TLB_ENTRIES-1) break;
	}

	assert(i<N_TLB_ENTRIES);
	tlb[i]=* p_translation;
	tlb[i].valid=1;
	adj_age(i);
}